package func_controller;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Mixer;

import cn.rmshadows.joffice.JofficeToolKit;
import cn.rmshadows.joffice.PrimaryController;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextArea;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.AnchorPane;
import vosk.VoskUtil;

public class VoskController implements Initializable {
	// 注意：TextArea输入中文需要-Djdk.gtk.version=2参数
	@FXML
	AnchorPane cur_ap;
	@FXML
	SplitPane fv_sp;
	@FXML
	AnchorPane fv_ap;
	@FXML
	TextArea vosk_ta_result;
	@FXML
	AnchorPane fo_ap;
	// 识别按钮
	@FXML
	Button vosk_rec_btn;
	// 识别文件按钮
	@FXML
	Button vosk_file_btn;
	// 一键复制
	@FXML
	Button vosk_bt_copy;
	// 使用帮助
	@FXML
	Button vosk_help;
	// 清空
	@FXML
	Button vosk_clear;
	// Model下拉框
	@FXML
	ComboBox<String> combo_model;
	// Mixers下拉框
	@FXML
	ComboBox<String> combo_mixers;
	// 模型名称
	static LinkedList<String> m_name = new LinkedList<>();
	// 模型路径
	static LinkedList<String> m_path = new LinkedList<>();
	// Mixer名称
	static LinkedList<String> mixer_name = new LinkedList<>();
	// Mixer列表
	static LinkedList<Mixer.Info> mixer_info = new LinkedList<>();
	// 字体大小
	static int font_size;
	static String font_css = "-fx-font-size: ";
	// 获取结果
	public static TextArea vosk_get;
	static Button s_vosk_rec;
	static Button s_vosk_file;
	// 拖拽文件功能
	// 拖拽文件是否成功
	static boolean is_drag_success = false;
	// vosk识别结果文本框的文字
	static String vosk_text = null;
	// 拖拽文件列表
	List<File> drag_files = null;

	// 是否正在识别
	public static boolean is_vosking = false;

	// 初始化
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		m_name.clear();
		m_path.clear();
		mixer_info.clear();
		mixer_name.clear();
		// 检查conf文件夹配置
		File voskConf = new File("conf/00-vosk.conf");
		if (!voskConf.exists()) {
			try {
				voskConf.createNewFile();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
		// 读取Mixers
		mixer_info = JofficeToolKit.displayMixerInfo();
		for (Mixer.Info i : mixer_info) {
			mixer_name.add(i.getName());
		}
		ObservableList<String> mixer_list = FXCollections.observableList(mixer_name);
		combo_mixers.setItems(mixer_list);
		int default_mixer = mixer_info.indexOf(AudioSystem.getMixer(null).getMixerInfo());
		combo_mixers.getSelectionModel().select(default_mixer);
		// 读取model列表
		LinkedHashMap<String, String> models = JofficeToolKit.loadConf(voskConf.getAbsolutePath(), "=");
		for (String i : models.keySet()) {
			m_name.add(i);
		}
		for (String value : models.values()) {
			m_path.add(value);
		}
		ObservableList<String> model_list = FXCollections.observableList(m_name);
		combo_model.setItems(model_list);
		combo_model.getSelectionModel().selectFirst();

		// 设置文本框字体
		vosk_get = vosk_ta_result;
		vosk_ta_result.setWrapText(true);
		font_size = 18;
		String tfs = font_css + font_size;
		vosk_ta_result.setStyle(tfs);
		tfs = null;
		KeyCombination inc = new KeyCodeCombination(KeyCode.EQUALS, KeyCombination.CONTROL_DOWN);
		KeyCombination dec = new KeyCodeCombination(KeyCode.MINUS, KeyCombination.CONTROL_DOWN);

		// 字体增大、缩小
		vosk_ta_result.setOnKeyPressed(e -> {
			if (inc.match(e)) {
				font_size += 1;
				String fs = font_css + font_size;
				vosk_ta_result.setStyle(fs);
			} else if (dec.match(e)) {
				if (font_size != 1) {
					font_size -= 1;
					String fs = font_css + font_size;
					vosk_ta_result.setStyle(fs);
				}
			}
		});

		// 设置按钮
		// 文件识别
		vosk_file_btn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent event) {
				if (!is_vosking) {
					if (event.getButton() == MouseButton.PRIMARY) {
						// 单线程识别
						String file_path = VoskUtil.convert2VoskWaveFile(
								vosk_ta_result.getText().replaceAll("\n", "").replaceAll("\r\n", ""));
						// 如果文件读取出错
						if (file_path == null) {
							vosk_bt_status(1, false);
							vosk_ta_result.setText("文件读取出错。");
							return;
						}
						int selected_model = combo_model.getSelectionModel().getSelectedIndex();
						String model_path = m_path.get(selected_model);
//						System.out.println(model_path);
						// 启用一个新得线程识别
						Thread vosk_file_thread = new Thread(new Runnable() {
							@Override
							public void run() {
								vosk.VoskUtil.voskVoiceFile(file_path, model_path);
							}
						});
						vosk_file_thread.start();
					} else if (event.getButton() == MouseButton.SECONDARY) {
						// 多线程识别
						String file_path = VoskUtil.convert2VoskWaveFile(
								vosk_ta_result.getText().replaceAll("\n", "").replaceAll("\r\n", ""));
						// 如果文件读取出错
						if (file_path == null) {
							vosk_bt_status(1, false);
							vosk_ta_result.setText("文件读取出错。");
							return;
						}
						int selected_model = combo_model.getSelectionModel().getSelectedIndex();
						String model_path = m_path.get(selected_model);
						Thread vosk_file_thread = new Thread(new Runnable() {
							@Override
							public void run() {
								vosk.VoskUtil.voskLongVoiceFile(file_path, model_path);
							}
						});
						vosk_file_thread.start();
					}
				} else {
					// 如果正在识别、打断
					is_vosking = false;
					vosk_file_btn.setText("文件识别");
				}
			}
		});
		s_vosk_rec = vosk_rec_btn;
		s_vosk_file = vosk_file_btn;

		PrimaryController.fv_change = false;

		System.out.println(Thread.currentThread());

	}

	// 拖拽文件功能 https://docs.oracle.com/javafx/2/drag_drop/jfxpub-drag_drop.htm
	/**
	 * 等效替换 拖拽功能 vosk_ta_result.setOnDragEntered(new EventHandler <DragEvent>() {
	 * public void handle(DragEvent event) { // the drag-and-drop gesture entered
	 * the target System.out.println("onDragEntered"); // show to the user that it
	 * is an actual gesture target if (event.getGestureSource() != vosk_ta_result &&
	 * event.getDragboard().hasString()) { vosk_ta_result.setText("来了老弟!!"); }
	 * 
	 * event.consume(); } });
	 */
	// 拖拽进入，备份原有文字
	@FXML
	private void voskOnDragEntered(DragEvent event) {
//		System.out.println("onDragEntered");
		vosk_text = vosk_ta_result.getText();
		// 如果正在识别，则无法拖拽释放
		if (is_vosking) {
			vosk_ta_result.setText("请先停止Vosk识别再进行文件拖拽！");
			is_drag_success = false;
		} else {
			vosk_ta_result.setText("拖拽文件到这里进行识别(单线程)。");
		}
		event.consume();
	}

	// 正在拖拽
	@FXML
	private void voskOnDragOver(DragEvent event) {
//		System.out.println("onDragOver");
		if (is_vosking) {
			is_drag_success = false;
		} else {
			// 没有这句话就没法DragDropped
			event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
		}
		event.consume();
	}

	// 拖拽释放，检测是否是文件
	@FXML
	private void voskOnDragDropped(DragEvent event) {
		/* data dropped */
//		System.out.println("onDragDropped");

		Dragboard db = event.getDragboard();
		if (db.hasFiles()) {
//            vosk_ta_result.setText(db.getFiles().toString());
			System.out.println(db.getFiles().toString());
			drag_files = db.getFiles();

			is_drag_success = true;
		} else {
			drag_files = null;
			is_drag_success = false;
		}
		/*
		 * let the source know whether the string was successfully transferred and used
		 */
		event.setDropCompleted(is_drag_success);
		event.consume();
	}

	// 拖拽结束，成功即开始识别，失败则不做处理
	@FXML
	private void voskOnDragExited(DragEvent event) {
		/* the drag-and-drop gesture entered the target */
//		System.out.println("onDragExited");
		if (is_drag_success) {
			// 开始进行拖拽识别
			int selected_model = combo_model.getSelectionModel().getSelectedIndex();
			String model_path = m_path.get(selected_model);
			// 输入的识别参数:文件对象、文件流
			LinkedHashMap<File, Integer> task_drag_to = new LinkedHashMap<>();
			// 添加列表 File id
			int t_id = 0;
			for (File i : drag_files) {
				System.out.println("Log:添加拖拽的文件==>>" + i.getName());
				task_drag_to.put(new File(VoskUtil.convert2VoskWaveFile(i.getAbsolutePath())), t_id);
				t_id += 1;
			}
//			System.out.println(task_drag_to);
			vosk_ta_result.setText("正在识别……");
			// 启用一个新得线程识别
			Thread vosk_drag_files_thread = new Thread(new Runnable() {
				@Override
				public void run() {
					vosk.VoskUtil.voskVoiceFiles(task_drag_to, model_path);
				}
			});
			vosk_drag_files_thread.start();
			is_vosking = true;
		} else {
			// 失败
			if (!is_vosking) {
				vosk_ta_result.setText("获取文件失败。");
			}
			// 还原
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					vosk_ta_result.setText(vosk_text);
				}
			}).start();
		}
		event.consume();
	}

	// 识别麦克风
	@FXML
	private void voskMic() {
		if (!is_vosking) {
			// 选择的模型
			int selected_model = combo_model.getSelectionModel().getSelectedIndex();
			String model_path = m_path.get(selected_model);
			// 获取选择的设备
			int selected = combo_mixers.getSelectionModel().getSelectedIndex();
			Mixer.Info selected_mixer = mixer_info.get(selected);
			// 启用一个新得线程识别
			Thread vosk_mic_thread = new Thread(new Runnable() {
				@Override
				public void run() {
					vosk.VoskUtil.voskSpecifiedMicrophone(selected_mixer, model_path);
				}
			});
			vosk_mic_thread.start();
		} else {
			// 如果正在识别、打断
			is_vosking = false;
			vosk_rec_btn.setText("Vosk识别");
		}
	}

	/**
	 * 复制识别结果
	 */
	@FXML
	private void copy() {
		vosk_ta_result.selectAll();
		vosk_ta_result.copy();
	}

	/**
	 * 清空文本框
	 */
	@FXML
	private void clear(MouseEvent event) {
		if (event.getButton() == MouseButton.PRIMARY) {
			vosk_ta_result.clear();
		} else if (event.getButton() == MouseButton.SECONDARY) {
			String text_to_delete_space = vosk_ta_result.getText();
			vosk_ta_result.setText(text_to_delete_space.replaceAll(" ", ""));
		}
	}

	/**
	 * 显示帮助信息
	 */
	@FXML
	private void voskHelp() {
		vosk_ta_result.setText("概述：Vosk是一款开源的离线语音识别框架。\n" + "使用前的准备：\n"
				+ "1.从 https://alphacephei.com/vosk/models 上下载语音识别模型，解压后重命名为model_（你自定义的名称）。\n"
				+ "2.新建conf文件夹，在conf文件夹中新建配置文件“00-vosk.conf。配置文件格式(用等于号分割)：“【语音识别模型名称】=【语音识别模型路径】””。\n" + "\n"
				+ "比如：我下载了一个中文识别模型，解压重命名为“model_zh”，放置于res目录下。再新建一个“00-vosk.conf”配置文件于conf目录下。"
				+ "“00-vosk.conf”配置文件内容是：中文=res/model_zh。这样，Joffice Vosk界面的模型选择中就会显示 中文 这个模型啦。\n" + "功能指南：\n"
				+ "1.Vosk识别：识别麦克风的语音。\n"
				+ "2.文件识别：识别文件。需要将文件路径写在文本框中，例如：/home/user/file.wav。注意：音频要求采样率16k，位数16，单声道，音频格式wav。鼠标主键单线程识别，副键单文件多线程识别（正确率降低）。\n"
				+ "3.一键复制：复制文本框中的内容。\n" + "4.清空/去空格：主键清空文本框，副键清除空格。\n"
				+ "5.选择模型：选择识别模型（conf/00-vosk.conf中设定的「自定义模型名称」=「模型路径」）\n"
				+ "6.选择设备：选择特定的麦克风输入。\n7.拖拽功能：支持拖拽多个文件，每个文件一个线程进行识别。\n" + "其他：\n"
				+ "如果Linux上文本框无法输入中文，请在java运行参数中加入：-Djdk.gtk.version=2");
	}

	/**
	 * 外部调整识别按钮状态的方法
	 * 
	 * @param type   0:Voskrec按钮 1:voskfile按钮
	 * @param status true正在识别 false识别结束
	 */
	public static void vosk_bt_status(int type, boolean status) {
		if (type == 0) {
			if (status) {
				is_vosking = true;
				Platform.runLater(new Runnable() {
					@Override
					public void run() {
						s_vosk_rec.setText("正在识别");
					}
				});
			} else {
				is_vosking = false;
				Platform.runLater(new Runnable() {
					@Override
					public void run() {
						s_vosk_rec.setText("Vosk识别");
					}
				});
			}
		} else if (type == 1) {
			if (status) {
				is_vosking = true;
				Platform.runLater(new Runnable() {
					@Override
					public void run() {
						s_vosk_file.setText("正在识别");
					}
				});
//				System.out.println(s_vosk_file);
			} else {
				is_vosking = false;
				Platform.runLater(new Runnable() {
					@Override
					public void run() {
						s_vosk_file.setText("文件识别");
					}
				});
			}
		}
	}

}